﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

using Pibby.Core.IO;
using Pibby.Core.Persistence;


using invrt;
using Pibby.Core.PortableMedia;
using dogd;

namespace Pibby.Core.Models
{

	public class PodcatcherModel : IPodcatcherModel
	{
		#region Events

		//public event EventHandler SettingsLoaded;
		public event EventHandler SettingsChanged;

		public event EventHandler<RemovableDriveEventArgs> PortableMediaArrived;

		public event EventHandler<RemovableDriveEventArgs> PortableMediaRemoved;

		#endregion Events

		#region Constructor

		public PodcatcherModel(IUserSettings settings, IPodcastRepositoryFactory repositoryFactory)
		{
			this.Settings = new PerUserSettings(settings);
			this.RepositoryFactory = repositoryFactory;

			var factory = InversionOfController.Factory.CreateTypeOf<IFolderFactory>();

			this.DownloadFolder = factory.GetFolder (this.Settings.DownloadFolder);

			IPodcastRepository repository = this.RepositoryFactory.Create();
			var deviceSettings = repository.LoadMediaSettings();

			this.PortableMedia = new PortableMediaDevice()
			{
				Enabled = deviceSettings.Enabled,
				FriendlyName = deviceSettings.VolumeName,
				VolumeName = deviceSettings.VolumeName,
				Folder = deviceSettings.BaseFolder,
				Organization = deviceSettings.OrganizationOption,
				EjectOption = deviceSettings.EjectOption
			};

			//var podcasts = repository.LoadPodcasts();

			//foreach (var podcast in podcasts)
			//{
			//    var episodes = repository.LoadEpisodes(podcast.PrimaryKey);

			//    foreach (var episode in episodes)
			//    {
			//        episode.Stage = EpisodeLifecycle.Unknown;
			//        repository.Save(episode);
			//    }
			//}

			
		}

		#endregion Constructor

		#region IPodcatcherModel

		public IUserSettings Settings
		{
			get;
			private set;
		}

		private IPodcastRepositoryFactory RepositoryFactory
		{
			get;
			set;
		}

		public PortableMediaDevice PortableMedia
		{
			get;
			private set;
		}

        public void ReloadSettings()
        {
            this.Settings = PerUserSettings.Reload(this.Settings.Path);
        }

		public PodcastCollection LoadSubscriptions()
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();
			return repository.LoadPodcasts();
		}

		public Podcast LoadSubscription(int dbKey)
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();
			return repository.LoadPodcast(new Key(dbKey));
		}

		public EpisodeCollection LoadEpisodes()
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();

			var episodes = repository.LoadEpisodes(EpisodeLifecycle.NotDownloaded);
			var classifier = new EpisodeClassifier(DateTime.Today);

			foreach (var episode in episodes)
			{
				classifier.Classify(episode);
			}

			return episodes;
		}

		public EpisodeCollection LoadEpisodes(EpisodeLifecycle lifecycle)
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();

			return repository.LoadEpisodes(lifecycle);
		}

		public EpisodeCollection LoadEpisodes(Podcast podcast)
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();
			var episodes = repository.LoadEpisodes(podcast.PrimaryKey);

			var classifier = new EpisodeClassifier(DateTime.Today);

			foreach (var episode in episodes)
			{
				classifier.Classify(episode);
			}

			return episodes;
		}

		public Episode LoadEpisode(int dbKey)
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();
			var episode = repository.LoadEpisode(new Key(dbKey));

			var classifier = new EpisodeClassifier(DateTime.Today);

			classifier.Classify(episode);

			return episode;
		}

		public void UpdateEpisode(Episode e)
		{
			Contract.EnsureIsNotNull(e, "Episode is null");

			IPodcastRepository repository = this.RepositoryFactory.Create();
			{
				repository.Save(e);
			}
		}


		public DownloadableCollection DownloadableFeeds
		{
			get
			{
				var feeds = new DownloadableCollection();

				IPodcastRepository repository = this.RepositoryFactory.Create();
				{
					foreach (var p in repository.LoadPodcasts())
					{
						feeds.Add(CreateFeedDownload(p));
					}

					return feeds;
				}
			}
		}

		private IFolder DownloadFolder
		{
			get;
			set;
		}

		public DownloadableCollection LoadEpisodesForDownload(IEnumerable<int> dbKeys)
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();
			var downloads = new DownloadableCollection();

			// load all the episodes...
			var episodes = repository.LoadEpisodes(dbKeys);

			// we need a download folder for this ....
			foreach (var episode in episodes)
			{
				string folder = string.IsNullOrEmpty(episode.PodcastName) ? episode.SubscriptionID.ToString() : episode.PodcastName;
				var podcastFolder = this.DownloadFolder.Append(folder);

				var d = new Downloadable()
				{
					Uri = episode.Feed,
					TargetFolder = podcastFolder.Path,
					Id = episode.Id,
					FriendlyName = string.Format("Getting \'{0}\' from \'{1}\'", episode.Name, episode.PodcastName),
					FileSize = episode.FileSize,
					Tag = new EpisodeDownloadCookie(episode.PrimaryKey.Value)
				};

				downloads.Add(d);
			}

			return downloads;
		}

		private Downloadable CreateFeedDownload(Podcast podcast)
		{
			return new Downloadable()
			{
				Id = podcast.Id,
				Uri = podcast.Feed,
				FriendlyName = string.Format("Refreshing \'{0}\' from Rss feed", podcast.Name),
				InMemoryDownload = true,
				Tag = new PodcastRefreshCookie(podcast.PrimaryKey.Value)
			};
		}

		public void SaveSettings()
		{
			EventHandler changed = this.SettingsChanged;

			if (changed != null)
			{
				changed(this, EventArgs.Empty);
			}
		}


		public void CreateFeed(Uri uri, string suggestedName, string feedContent)
		{
			if (uri == null)
				throw new ArgumentException("Uri cannot be null", "uri");

			if (string.IsNullOrEmpty(feedContent))
				throw new ArgumentException("Feed cannot be null", "feedContent");

			try
			{
				var factory = new Feeds.FeedFactory();
				var feed = factory.CreateFeed(feedContent);

				if (string.IsNullOrEmpty(suggestedName))
					suggestedName = feed.Title;

				var podcast = new Podcast(suggestedName, uri);

				podcast.RefreshInfo(feed);

				IPodcastRepository repository = this.RepositoryFactory.Create();
				repository.Save(podcast);

				var builder = new EpisodeCollectionBuilder();
				builder.Build(feed, podcast, DateTime.MinValue);

				repository.Import(builder.Episodes);
			}
			catch (Feeds.FeedReadException)
			{
				string xmlFile = FileNameSanitizer.FixFileName("Unknown_Podcast" + DateTime.Now.ToString() + ".xml");

				string fileName = this.DownloadFolder.PathToFile(xmlFile);

				if (System.IO.File.Exists(fileName))
					System.IO.File.Delete(fileName);

				var factory = InversionOfController.Factory.CreateTypeOf<ITextWriterFactory>();

				using (var writer = factory.FromFile(fileName))
				{
					writer.Write(feedContent);
				}

				throw;
			}
			catch (System.Xml.XmlException)
			{
			}
		}

		public void UpdateFeed(int dbKey, string feedContent)
		{
			if (string.IsNullOrEmpty(feedContent))
			    throw new ArgumentException("Feed cannot be null", "feedContent");

			using (var reader = new StringReader(feedContent))
			{
				IPodcastRepository repository = this.RepositoryFactory.Create();
				{
					Podcast podcast = null;
					EpisodeCollection episodes = null;

					try
					{

						podcast = repository.LoadPodcast(new Key(dbKey));
						episodes = repository.LoadEpisodes(podcast.PrimaryKey);
						episodes.SortByDate();
					}
					catch (CannotReadObjectException)
					{
					}

					try
					{
						var factory = new Feeds.FeedFactory();
						var feed = factory.CreateFeed(feedContent);

						DateTime lastUpdated = podcast.PublicationDate;

						var mostRecentEpisode = episodes.FirstOrDefault();

						if (mostRecentEpisode != null)
							lastUpdated = mostRecentEpisode.PublicationDate;

						bool detailsChanged = podcast.RefreshInfo(feed);

						var builder = new EpisodeCollectionBuilder();
						builder.Build(feed, podcast, lastUpdated);


						// either details have changed or whuffie has increased...
						if (detailsChanged || builder.NewEpisodes > 0)
							repository.Save(podcast);

						repository.Import(builder.Episodes);
					}
					catch (Feeds.FeedReadException)
					{
						SaveRssContent(feedContent, podcast);

						throw;
					}
					catch (System.Xml.XmlException)
					{
					}
				}
			}
		}

		public void UpdateEpisodes(IEnumerable<int> dbKeys, EpisodeLifecycle stage)
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();
			{
				var episodes = repository.LoadEpisodes(dbKeys);

				if (episodes.Count > 0)
				{
					foreach (var epsiode in episodes)
					{
						epsiode.Stage = stage;

						repository.Save(epsiode);
					}
				}
			}
		}

		private void SaveRssContent(string feedContent, Podcast podcast)
		{
			string xmlFile = FileNameSanitizer.FixFileName("Unknown_Podcast" + DateTime.Now.ToString() + ".xml");

			if (podcast != null)
				xmlFile = podcast.SafeName + ".xml";

			string fileName = this.DownloadFolder.PathToFile(xmlFile);

			if (System.IO.File.Exists(fileName))
				System.IO.File.Delete(fileName);

			var factory = InversionOfController.Factory.CreateTypeOf<ITextWriterFactory>();

			using (var writer = factory.FromFile(fileName))
			{
				writer.Write(feedContent);
			}
		}

		#endregion IPodcatcherModel

		public void ImportSubscriptions(TextReader reader)
		{
			var importer = new PodcastCollectionImporter();

			importer.Load(reader);

			IPodcastRepository repository = this.RepositoryFactory.Create();
			
			foreach (var podcast in importer.Podcasts)
			{
				repository.Save(podcast);
			}
		}

		public void ExportSubscriptions(TextWriter writer)
		{
			var exporter = new PodcastCollectionExporter();

			exporter.Podcasts = this.LoadSubscriptions();
			exporter.Save(writer);
		}

		public void AddPodcast(Podcast newPodcast)
		{
			throw new NotImplementedException();
		}

		public void RemovePodcast(Podcast existingPodcast)
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();
			repository.Remove(existingPodcast);
		}

		public PodcastCollection SearchPodcasts(string searchTerm)
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();
			return repository.SearchPodcasts(searchTerm);
		}

		public EpisodeCollection SearchEpisodes(string podcastGuid, string searchTerm)
		{
			//IPodcastRepository repository = this.RepositoryFactory.Create();
			//var podcast = repository.LoadPodcast(podcastGuid);

			//if (podcast != null)
			//{
			//    return repository.SearchEpisodes(podcast.PrimaryKey, searchTerm);
			//}

			return null;
		}

		public EpisodeCollection SearchEpisodes(string searchTerm)
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();
			return repository.SearchEpisodes(searchTerm);
		}

		public void CheckPortableMediaStatus()
		{
			IDrive drive = VolumeFinder.FindVolume(this.PortableMedia.VolumeName);

			if (drive != null)
			{
				CheckPortableMediaArrived(new RemovableDriveEventArgs(
													drive.Name,
													this.PortableMedia.VolumeName,
													RemovableDriveStatus.Connected));
			}
			else
			{
				CheckPortableMediaRemoved(new RemovableDriveEventArgs(
													string.Empty,
													this.PortableMedia.VolumeName,
													RemovableDriveStatus.Disconnected));
			}
		}

		public void CheckPortableMediaArrived(RemovableDriveEventArgs e)
		{
			if (!this.PortableMedia.IsConnected())
			{
				this.PortableMedia.CheckConnected(e);

				if (this.PortableMedia.IsConnected())
				{
					var arrived = PortableMediaArrived;

					if (arrived != null)
						arrived(this, e);
				}
			}
		}

		public void CheckPortableMediaRemoved(RemovableDriveEventArgs e)
		{
			this.PortableMedia.CheckConnected(e);

			if (this.PortableMedia.IsDisconnected())
			{
				var removed = PortableMediaRemoved;

				if (removed != null)
					removed(this, new RemovableDriveEventArgs(e.DriveName, 
																this.PortableMedia.VolumeName, 
																e.Status));
			}
		}

		public TransferableCollection TransferrableContent
		{
			get
			{
				var builder = new TransferableCollectionBuilder();

				var factory = InversionOfController.Factory.CreateTypeOf<IFolderFactory>();

				var synchFolder = factory.GetFolder(this.PortableMedia.Folder);

				IPodcastRepository repository = this.RepositoryFactory.Create();

				return builder.Build(repository,
									this.DownloadFolder,
									synchFolder, this.PortableMedia);
			}
		}

		public void RenamePodcast(Podcast existingPodcast, string newName)
		{
			IPodcastRepository repository = this.RepositoryFactory.Create();
			var podcast = repository.LoadPodcast(existingPodcast.PrimaryKey);

			podcast.Name = newName;

			repository.Save(podcast);
		}

		public void UpdatePodcast(Podcast existingPodcast)
		{
			Contract.EnsureIsNotNull(existingPodcast, "existingPodcast is null");

			IPodcastRepository repository = this.RepositoryFactory.Create();
			repository.Save(existingPodcast);
		}


		public void RebaseReputations()
		{
			// get most recent...
			IPodcastRepository repository = this.RepositoryFactory.Create();
			var podcasts = repository.LoadPodcasts();

			podcasts.Sort();

			if (podcasts.Count > 1 && podcasts[0].Score > 0)
			{
				int highestScore = podcasts[0].Score;

				foreach (var podcast in podcasts)
				{
					podcast.Whuffie.Score = (podcast.Whuffie.Score * 100) / highestScore;
					repository.Save(podcast);
				}
			}
		}
	}
}
